package me.weijing.common.http;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import javax.net.ssl.X509TrustManager;

import me.weijing.common.http.RequestParams.FileWrapper;
import android.os.Build;
import android.text.TextUtils;

public class HttpRequestPost extends HttpRequest {
    protected static final String BOUNDARY = "--0xKhTmLbOuNdArY";// 数据分割
    protected static final String ENTER = "\r\n";// 换行
    protected static final String DISPOSITION = "Content-Disposition: form-data; name=";
    protected static final String FILENAME = "; filename=";
    protected static final String CONTENTTYPE = "Content-Type: ";
    private String httpModel;

    public HttpRequestPost(String origUrl, String url, HttpCallback callback, Map<String, String> extraHeader, RequestParams params, String httpModel) {
        super(origUrl, url, callback, extraHeader, params);
        this.httpModel = httpModel;
    }

    public HttpRequestPost(String origUrl, String url, HttpCallback callback, Map<String, String> extraHeader, RequestParams params) {
        super(origUrl, url, callback, extraHeader, params);
    }

    int calcUploadContentLength() throws Exception {
        int totalFileLength = 0;
        if (params != null && params.fileParams != null) {
            Collection<FileWrapper> tmpFiles = params.fileParams.values();
            if (tmpFiles != null) {
                for (FileWrapper tf : tmpFiles) {
                    totalFileLength += tf.inputStream.available();
                }
            }
        }

        if (params != null && params.urlParams != null) {
            StringBuilder builder = new StringBuilder();
            for (Map.Entry<String, String> entry : params.urlParams.entrySet()) {
                builder.append(BOUNDARY);
                builder.append(ENTER);
                builder.append(DISPOSITION);
                builder.append("\"" + entry.getKey() + "\"");
                builder.append(ENTER);
                builder.append(ENTER);
                builder.append(entry.getValue());
                builder.append(ENTER);
            }
            totalFileLength += builder.toString().getBytes().length;
        }
        if (params != null && params.fileParams != null && params.fileParams.size() > 0) {
            for (Map.Entry<String, FileWrapper> entry : params.fileParams.entrySet()) {
                StringBuilder builder = new StringBuilder();
                FileWrapper file = entry.getValue();
                builder.append(BOUNDARY);
                builder.append(ENTER);
                builder.append(DISPOSITION);
                builder.append("\"" + entry.getKey() + "\"");
                builder.append(FILENAME);
                builder.append("\"" + file.getFileName() + "\"");
                builder.append(ENTER);
                builder.append(CONTENTTYPE);
                builder.append(getContentType(getSuffix(file.getFileName())));
                builder.append(ENTER);
                builder.append(ENTER);
                totalFileLength += builder.toString().getBytes().length;
                totalFileLength += ENTER.getBytes().length;
            }
        }
        String end = BOUNDARY + "--" + ENTER;
        byte[] endData = end.getBytes();
        totalFileLength += endData.length;
        return totalFileLength;
    }

    @Override
    public void execute() {
        HttpURLConnection conn = null;
        OutputStream os = null;
        InputStream in = null;
        InputStreamReader reader = null;
        ByteArrayOutputStream bos = null;
        boolean mark = false;
        if (TextUtils.isEmpty(url)) {
            if (callback != null) {
                callback.interpret(null);
            }
            return;
        }
        try {
            URL myUrl = new URL(url);
            conn = (HttpURLConnection) myUrl.openConnection();
            conn.setDoInput(true);// 允许输入
            conn.setDoOutput(true);// 允许输出
            conn.setUseCaches(false);// 不使用Cache
            conn.setInstanceFollowRedirects(true);
            int contentLength = 0;
            byte[] postByteArray = null;

            if (httpModel != null && httpModel.equals("put") && params != null) {
                params.put("PUT", true);
            }
            conn.setRequestMethod("POST");

            // head
            if (mExtraHeader != null) {
                for (Map.Entry<String, String> entry : mExtraHeader.entrySet()) {
                    conn.setRequestProperty(entry.getKey(), entry.getValue());
                }
            }

            // EOF Exception in HTTPRequest (Android 4.1) bug fix it

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                try {
                    conn.setRequestProperty("Connection", "close");
                    System.setProperty("http.keepAlive", "false");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                System.setProperty("http.keepAlive", "true");
            }

            /* conn.setRequestProperty("Connection", "Keep-Alive"); */

            conn.setRequestProperty("Charset", "UTF-8");
            conn.setRequestProperty("deviceId", deviceId);
            if (params != null && params.fileParams != null && params.fileParams.size() > 0) {
                conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=0xKhTmLbOuNdArY");
                contentLength = calcUploadContentLength();
            } else {
                conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                postByteArray = params.getParamString().getBytes();
                contentLength = postByteArray.length;
            }
            conn.setConnectTimeout(TIMEOUT_CONNECT);
            conn.setReadTimeout(TIMEOUT_READ);

            if (contentLength > 256 * 1024) {
                conn.setChunkedStreamingMode(0);
            } else {
                conn.setFixedLengthStreamingMode(contentLength);
            }
            conn.connect();
            // body
            os = conn.getOutputStream();
            boolean isProgress = false;// 是否显示进度

            if (params != null && params.urlParams != null && (params.fileParams == null || params.fileParams.size() == 0)) {
                os.write(postByteArray);
                os.flush();
            } else {
                if (params != null && params.urlParams != null) {
                    StringBuilder builder = new StringBuilder();
                    Set<Map.Entry<String, String>> set = params.urlParams.entrySet();
                    Iterator<Map.Entry<String, String>> iterator = set.iterator();
                    while (iterator.hasNext()) {
                        Map.Entry<String, String> entry = iterator.next();
                        builder.append(BOUNDARY);
                        builder.append(ENTER);
                        builder.append(DISPOSITION);
                        builder.append("\"" + entry.getKey() + "\"");
                        builder.append(ENTER);
                        builder.append(ENTER);
                        builder.append(entry.getValue());
                        builder.append(ENTER);
                    }
                    os.write(builder.toString().getBytes());
                    os.flush();
                }

                if (params != null && params.fileParams != null && params.fileParams.size() > 0) {
                    if (null != callback && callback instanceof HttpProgressCallback) {
                        isProgress = true;
                    }
                    int uploadedLen = 0;
                    int oldProgress = 0;
                    byte[] buffer = new byte[1024];
                    Set<Map.Entry<String, FileWrapper>> set = params.fileParams.entrySet();
                    Iterator<Map.Entry<String, FileWrapper>> iterator = set.iterator();
                    while (iterator.hasNext()) {
                        Map.Entry<String, FileWrapper> entry = iterator.next();
                        StringBuilder builder = new StringBuilder();
                        FileWrapper file = entry.getValue();
                        builder.append(BOUNDARY);
                        builder.append(ENTER);
                        builder.append(DISPOSITION);
                        builder.append("\"" + entry.getKey() + "\"");
                        builder.append(FILENAME);
                        builder.append("\"" + file.getFileName() + "\"");
                        builder.append(ENTER);
                        builder.append(CONTENTTYPE);
                        builder.append(getContentType(getSuffix(file.getFileName())));
                        builder.append(ENTER);
                        builder.append(ENTER);
                        os.write(builder.toString().getBytes());
                        os.flush();
                        InputStream is = file.inputStream;

                        int len = 0;
                        int icount = 0;

                        while ((len = is.read(buffer)) != -1) {
                            if (null != callback && callback.isCanceled()) {
                                mark = true;
                                return;
                            }
                            os.write(buffer, 0, len);
                            icount++;
                            if (isProgress) {
                                uploadedLen += len;
                                if (null != callback) {
                                    int newProgress = uploadedLen * 100 / contentLength;
                                    if (oldProgress != newProgress && 100 != newProgress) {
                                        ((HttpProgressCallback) callback).progressPublish(newProgress);
                                    }
                                    oldProgress = newProgress;
                                }
                            }
                            if (icount > UPLOAD_FILE_SIZE) {
                                os.flush();
                                icount = 0;
                            }
                        }
                        os.write(ENTER.getBytes());
                        os.flush();
                    }
                }
                String end = BOUNDARY + "--" + ENTER;
                byte[] endData = end.getBytes();
                os.write(endData);
                os.flush();
            }

            int responseCode = conn.getResponseCode();
            if (isProgress) {
                if (null != callback) {
                    ((HttpProgressCallback) callback).progressPublish(100);
                }
            }
            if (responseCode == HttpURLConnection.HTTP_OK) {
                bos = new ByteArrayOutputStream();
                in = conn.getInputStream();
                byte[] data = null;
                if (data == null) {
                    data = new byte[1024];
                }
                int icount = 0;
                while (true) {
                    int iret = in.read(data);
                    if (iret < 0)
                        break;
                    bos.write(data, 0, iret);
                    icount++;
                    if (icount > UPLOAD_FILE_SIZE) {
                        bos.flush();
                        icount = 0;
                    }
                }
                bos.flush();
                if (callback != null) {
                    mark = true;
                    callback.interpret(bos.toByteArray());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            if (null != params && params.fileParams != null && params.fileParams.size() > 0) {
                Collection<FileWrapper> tmpFiles = params.fileParams.values();
                for (FileWrapper tf : tmpFiles) {
                    try {
                        tf.inputStream.close();
                    } catch (Throwable e) {
                        e.printStackTrace();
                    }
                }
            }
            if (bos != null) {
                try {
                    bos.close();
                } catch (Throwable e) {

                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (Throwable e) {
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (Throwable e) {
                }
            }
            if (os != null) {
                try {
                    os.close();
                } catch (Throwable e) {
                }
            }
            if (conn != null) {
                try {
                    conn.disconnect();
                } catch (Throwable e) {

                }
                conn = null;
            }
            if (!mark && callback != null) {
                try {
                    callback.serverFail(AsyncHttpRequestBase.FAILTYPE_SERVER_FAIL);
                } catch (Throwable e) {
                }
            }
        }
    }

    /**
     * 从文件路径获取文件后
     * 
     * @param filePathName
     * @return
     */
    protected String getSuffix(String filePathName) {
        int index = filePathName.lastIndexOf('.');
        if (index != -1 && (index + 1) < filePathName.length()) {
            return filePathName.substring(index + 1);
        }
        return "";
    }

    /**
     * 根据后缀名获取Content-Type类型(比如image/jpep、application/octet-stream等类
     * 
     * @param suffix
     * @return
     */
    protected String getContentType(String suffix) {
        if ("jpg".equals(suffix) || "jpeg".equals(suffix)) {
            return "image/jpeg";
        } else if ("png".equals(suffix)) {
            return "image/png";
        } else if ("gif".equals(suffix)) {
            return "image/gif";
        } else if ("amr".equals(suffix)) {
            return "application/octet-stream";
        } else if ("3gp".equals(suffix)) {
            return "video/3gpp";
        } else if ("mp4".equals(suffix) || "mp4v".equals(suffix) || "mpg4".equals(suffix)) {
            return "video/mp4";
        }
        return "application/octet-stream";
    }

    class MyX509TrustManager implements X509TrustManager {

        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    }

    class MyHostnameVerifier implements HostnameVerifier {

        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }
}
